<?php

/**
 * Db module
 *
 * @author coder_
 */

Class Db extends _Module
{
    private static $instance;

    private $dependencies = array('Config', 'Url');
    private $host;
    private $user;
    private $pass;
    private $name;
    private $handle;

    private function __construct()
    {
    }

    private function __clone()
    {
    }

    public static function &getInstance()
    {
        if (is_null(self::$instance)) {
            self::$instance = new self;
        }
        return self::$instance;
    }

    public function initialize()
    {
        $config = getModule('Config');
        $this->host = $config->getDBHost();
        $this->user = $config->getDBUser();
        $this->pass = $config->getDBPassword();
        $this->name = $config->getDBName();

        try {
            $this->handle = new PDO('mysql:host=' . $this->host . ';dbname=' . $this->name, $this->user, $this->pass, array(
                PDO::ATTR_PERSISTENT => true
            ));
        } catch(PDOException $e) {
            echo 'Connection failed: ' . $e->getMessage();
        }
    }

    public function getDependencies()
    {
        return $this->dependencies;
    }

    /**
     * Returns array with available db drivers
     *
     * @return array
     */
    public function getAvailableDrivers()
    {
        return PDO::getAvailableDrivers();
    }

    /**
     * Returns array with table names in current db
     *
     * @return array
     */
    public function getTables()
    {
        $result = $this->handle->query('SHOW TABLES');

        return $result->fetchAll(PDO::FETCH_COLUMN);
    }

    /**
     * Returns first row from query's results
     * Fetch styles:
     * 'ASSOC'
     * 'NUMERIC'
     * 'OBJECT'
     *
     * @return mixed
     */
    public function getRow($sql, $params = array(), $type = 'ASSOC')
    {
        $sth = $this->query($sql, $params);

        $fetchStyle = PDO::FETCH_ASSOC;

        switch($type)
        {
            case 'NUMERIC':
                    $fetchStyle = PDO::FETCH_NUM;
                break;
            case 'OBJECT':
                    $fetchStyle = PDO::FETCH_OBJ;
                break;

        }

        return $sth->fetch($fetchStyle);
    }

    /**
     * Returns single column from query's results
     *
     * @return array
     */
    public function getColumn($sql, $params = array())
    {
        $sth = $this->query($sql, $params);

        return $sth->fetchAll(PDO::FETCH_COLUMN);
    }

    /**
     * Returns result set for query
     *
     * @return void
     */
    public function getAll($sql, $params = array())
    {
        $sth = $this->query($sql, $params);

        return $sth->fetchAll();
    }

    /**
     * Returns single result
     *
     * @return mixed
     */
    public function getSingle($sql, $params = array())
    {
        $sth = $this->query($sql, $params);

        return $sth->fetchColumn();
    }

    /**
     *  Makes query without results
     *  Returns affected rows count
     *
     * @param $sql
     * @return int
     */
    public function exec($sql, $params = array())
    {
        $sth = $this->query($sql, $params);
        // TODO: later add success checking and reporting
    }

    /**
     * Returns last inserted row autoincrement id
     *
     * @return int
     */
    public function getLastInsertedId()
    {
        return $this->handle->lastInsertId();
    }

    /**
     * Function to quote sql
     *
     * @param $sql
     * @return string
     */
    public function quote($sql)
    {
        return $this->handle->quote($sql);
    }

    /**
     * Query preparation and parameter binding function
     *
     * @param $sql
     * @param $params
     * @return mixed
     */
    private function query($sql, $params)
    {
        $sth = $this->handle->prepare($sql);

        if(isAssoc($params))
        {
            foreach($params as $name => &$value)
            {
                if(is_int($value)) {
                    $sth->bindParam(':' . $name, $value, PDO::PARAM_INT);
                } else if(is_string($value)) {
                    $sth->bindParam(':' . $name, $value, PDO::PARAM_STR);
                } else if(is_bool($value)) {
                    $sth->bindParam(':' . $name, $value, PDO::PARAM_BOOL);
                } else if(is_null($value)) {
                    $sth->bindParam(':' . $name, $value, PDO::PARAM_NULL);
                }
            }
        } else {
            $i = 1;
            foreach($params as &$value)
            {
                if(is_int($value)) {
                    $sth->bindParam($i++, $value, PDO::PARAM_INT);
                } else if(is_string($value)) {
                    $sth->bindParam($i++, $value, PDO::PARAM_STR);
                } else if(is_bool($value)) {
                    $sth->bindParam($i++, $value, PDO::PARAM_BOOL);
                } else if(is_null($value)) {
                    $sth->bindParam($i++, $value, PDO::PARAM_NULL);
                }
            }
        }

        $sth->execute();

        return $sth;
    }
}